iT邦幫忙

2023 iThome 鐵人賽

DAY 7
0

昨天我們快速地展示了 FastAPI 自動生成 API 文件的功能,接下來就會開始介紹怎麼進一步地設定裡面的內容,並聊聊兩個在實務開發上遇到的需求

  1. 同時準備兩個不同內容的 API 文件
  2. 使用 Reverse Proxy 後 API 文件會發生錯誤 (找不到某些檔案)

P.S. 這部分官網寫的比較分散一些

兩種 API 文件?

在昨天的文章有提到有兩種風格的 API 文件,分別是 Swagger UIReDoc,他們都是可以產生互動式 API 文件的免費開源工具。兩者的最大差別在排版邏輯,Swagger UI 是一整排列下來,可以再手動展開看對應的說明;而 ReDoc 則是有三個欄位 (畫面太窄的話右邊兩個會合併成一個),左邊是分類,中間是說明,右邊是程式碼範例。這兩種文件都有人在用,但我個人感覺比較常看到 Swagger UI 的介紹,平時也是比較習慣使用 Swagger UI 來快速確認 API 清單,因此,後續介紹會以 Swagger UI (也就是 /docs 上的版本) 為主。

大家可以參考這篇 2023 年的文章,它介紹了好幾個生成 API 文件的工具

API 說明的設定

Tag

官網說明

Tag 可以加在 include_router

from fastapi import APIRouter
from routers.api_v1.endpoints.user import router as user_router
from routers.api_v1.endpoints.data import router as data_router

router = APIRouter()

router.include_router(user_router, prefix="/user", tags=["user"])
router.include_router(data_router, prefix="/data", tags=["data"])

也可以加在各個 API 內

@router.get("/{user_id}", tags=["ithome"])
async def root(user_id):
    return {"message": f"Hello user {user_id}"}

API 文件就會變成這樣

但我比較喜歡一個 API 只用一個 tag,因為這樣才不會看起來有過多的 API

Summary、Description

官網說明

相信大家應該也有發現,每個 API 後面都有一個 Root,這是因為我每個 API 的 function 名稱都用 root (複製貼上的XD),只要把 function 名稱改掉就可以了。

# data.py
@router.get("")
async def get_data():
    return {"message": "Hello World"}

此外,也可以考慮直接設定 summarydescriptionsummary會直接取代原本用 function 名稱的說明

# data.py
@router.get("", summary="summmary", description="This is a short description")
async def get_data():
    return {"message": "Hello World"}

如果想要寫更長的說明,可以改成用多行註解的方式,同時,也可以使用 Markdown

# data.py
@router.get("", summary="summmary")
async def get_data():
    """
    # Markdwon
    ## Hello World

    Cool!
    - I can write markdwon message here
    - It's **awesome**
    """
    return {"message": "Hello World"}

已棄用 (Deprecated)

官網說明

有時候,我們會想要停用舊的 API,但不希望它直接從文件上消失,這時候可以考慮使用 deprecated 參數,把它設為 True 即可

# data.py
@router.post("", deprecated=True)
async def root():
    return {"message": "Hello World"}

文件本身的設定

文件簡介

官網說明

API 文件上方的介紹也可以修改

預設的樣子是這樣

經過這樣調整設定後

# main.py
description = """
# 詳細說明
這邊也支援 **Markdwon**
"""

app = FastAPI(
    title="酷酷的文件標題",
    version="1.2.3",
    summary="這只是 Demo 用的",
    description=description
)

就變成這樣了

文件位置 (路由)

官網說明

在某些情況下,我們不希望後端提供 API 文件 (資安上的考量),此時可以把 openapi_url 設成空字串,就可以讓 /docs/redoc 出現 API 文件的功能失效,只回傳 {"detail":"Not Found"}

# main.py
app = FastAPI(openapi_url="")

其他需求

上面介紹了寫 API 文件時,常用的設定方法,但有時候,我們會有一些比較不一樣的需求...

能不能同時有兩份不同的 API 文件?

乍聽之下這個要求很奇怪,為什麼需要有兩份?
但仔細想想,也不是不可能,例如:我們想要將兩個不同版本的 API 文件分開來

這時候就需要引入 Sub Application 的概念了

我們來看這段程式碼

# main.py
from fastapi import FastAPI

app = FastAPI()
@app.get("/app")
def read_main():
    return {"message": "Hello World from main app"}


subapi = FastAPI()
@subapi.get("/sub")
def read_sub():
    return {"message": "Hello World from sub API"}

app.mount("/subapi", subapi)

透過把 subapi mount 在 app 上,我們就可以同時有多個 API 文件

這個做法就是讓每個版本的 API 都是一個 sub application,之後管理上也會比較方便

API 版本管理之後有機會再來更進一步討論XD

代理伺服器

官方說明
在某些情況下,我們會在一個網域同時架設多個服務,此時我們會使用 Nginx、Traefik、Caddy 這類的工具,幫助我們進行反向代理 (Reverse proxy),讓我們可以根據路由來選擇後面對應的服務。

考量到篇幅問題,以及避免篇題,對於 Proxy Server 的介紹和設定,這邊就不著墨了...

一般的 API 沒什麼問題,但 API 文件卻會發生錯誤,這是因為 API 文件預設是去 /openapi.json 找檔案,而這個卻沒有被 Proxy server 處理到,自然就不會導到 FastAPI 後端了。

即使多設定規則,讓 /openapi.json 也導到 FastAPI 後端,你會發現後續還有其他檔案要處理,非常麻煩XD

因此,其中一個簡單粗暴的作法,就是把所有服務預設都導向 FastAPI 後端 (不加額外路由),其他服務則需要添加額外路由,並用該路由判斷要使用哪個服務。

但這個方法也僅限於同時只有一個 FastAPI 後端,如果有兩個不同服務 (或是專案) 都是使用 FastAPI,又要同時放在同一個網域下,就會出錯。

當然,這種情況也有一個粗暴的解法,那就是使用子網域。讓 Proxy Server 可以根據子網域來判斷該進哪一個服務。

那有沒有在不使用子網域,但同時有兩個 FastAPI 後端的解決辦法?
又或者是,有沒有把 FastAPI 放在某個路由下,卻又能讓 API 文件正常運作的方法?

有,那就是要設定 root_path

讓我們直接來 Demo 吧!
這邊我們用 Caddy 建立一個 reverse proxy,以下是 Caddyfile 內容

:1234 {
  handle_path /ithome/* {
    reverse_proxy /* localhost:8000
  }
}

上面這段的意思是,在 1234 port 遇到 /ithome 就把請求轉到 8000 port,並把路徑的 /ithome/ 取代成 / (也就是把 /ithome 去掉)

此時不論是 http://localhost:8000/api/v1/datahttp://localhost:1234/ithome/api/v1/data 都可以正確地觸發到 API,但是只有 http://localhost:8000/docs 正常顯示 API 文件,如下圖

接著我們在 main.py 加上 root_path 設定

app = FastAPI(root_path="/ithome")

接著再測試一次

最後成功改成讓 http://localhost:1234/ithome/docs 顯示 API 文件了

重點回顧

今天我們介紹了

  1. API 文件的進階設定
  2. 如何同時有多份不同內容的 API 文件
  3. 如何在反向代理時讓 API 文件可以正確顯示

最後面這段有關 Proxy Server 的內容其實是可以展開成獨立一篇,但考量之後決定只著重在 API 文件設定的部分,未來寫到 FastAPI 部屬的時候,再看看要不要寫完整一點
有問題歡迎留言討論~


上一篇
[Day 06] API 管理與 API 文件
下一篇
[Day 08] API 的 Response
系列文
FastAPI 開發筆記:從新手到專家的成長之路30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言